5.15. Справочник по Lua
Справочник по Lua
Основы языка
Версия языка
Актуальная стабильная версия — Lua 5.4. Она включает поддержку константных локальных переменных, улучшенное управление памятью, новые варианты поведения для __close и другие уточнения по сравнению с Lua 5.3 и 5.2.
Типы данных
Lua имеет восемь базовых типов данных:
- nil — значение, обозначающее отсутствие значения. Единственное возможное значение этого типа —
nil. - boolean — логический тип. Возможные значения:
true,false. - number — числовой тип. По умолчанию представляет собой число с плавающей точкой двойной точности (
double). В некоторых сборках может быть целочисленным (long long) или комбинированным. - integer — подтип числового типа, представляющий целые числа. Доступен как отдельный внутренний тип, но внешне проявляется как
number. - string — неизменяемая последовательность байтов. Поддерживает UTF-8, но не проверяет корректность кодировки. Может содержать нулевые байты.
- function — исполняемый блок кода. Может быть обычной функцией, анонимной функцией, замыканием или C-функцией.
- table — основная структура данных. Реализует ассоциативный массив, список, множество, объект и другие абстракции.
- userdata — блок памяти, управляемый из C. Используется при встраивании Lua в приложения.
- thread — легковесный поток выполнения (корутина). Не связан с системными потоками.
Примечание: Lua 5.4 официально перечисляет восемь типов, где
integerиfloatсчитаются подтипамиnumber.
Функция type(x) возвращает строковое представление типа значения x.
Переменные
Объявление и присваивание
Переменные в Lua не требуют объявления типа. Присваивание создаёт переменную в текущей области видимости.
x = 10
name = "Alice"
Локальные переменные
Локальные переменные объявляются с помощью ключевого слова local. Они существуют только в пределах блока, в котором объявлены.
local y = 20
local message = "Hello"
В Lua 5.4 можно объявить локальную переменную как константу с помощью <const>:
local <const> PI = 3.14159
Попытка изменить такую переменную вызывает ошибку компиляции.
Глобальные переменные
Любая переменная без local становится глобальной. Глобальные переменные хранятся в таблице _G.
global_var = "visible everywhere"
print(_G.global_var) -- то же самое
Множественное присваивание
Lua поддерживает одновременное присваивание нескольких значений:
a, b = 1, 2
x, y = y, x -- обмен значениями
Если значений больше, чем переменных — лишние игнорируются. Если переменных больше — недостающим присваивается nil.
Операторы
Арифметические операторы
+— сложение-— вычитание*— умножение/— деление (всегда возвращает число с плавающей точкой)//— целочисленное деление (округление к минус бесконечности)%— остаток от деления (a % b == a - (a // b) * b)^— возведение в степень-(унарный) — смена знака
Побитовые операторы (Lua 5.3+)
&— побитовое И|— побитовое ИЛИ~— побитовое исключающее ИЛИ<<— сдвиг влево>>— сдвиг вправо~(унарный) — побитовое НЕ (дополнение до двух)
Операторы сравнения
==— равенство~=— неравенство<,>,<=,>=— числовые и строковые сравнения
Сравнение таблиц, функций, userdata и корутин выполняется по ссылке.
Логические операторы
and— логическое Иor— логическое ИЛИnot— логическое НЕ
Эти операторы используют ленивую семантику и возвращают один из своих операндов, а не обязательно true/false.
Пример:
x = false or "default" --> x = "default"
y = 42 and "yes" --> y = "yes"
Конкатенация
..— конкатенация строк и чисел. Числа автоматически преобразуются в строки.
greeting = "Hello, " .. "world!" --> "Hello, world!"
message = "Value: " .. 42 --> "Value: 42"
Приоритет операторов (от высшего к низшему)
^- унарные
-,#,not,~ *,/,//,%+,-..- побитовые
<<,>>,&,~,| <,>,<=,>=,~=,==andor
Операторы одного уровня ассоциативны слева направо, кроме ^ и .., которые ассоциативны справа налево.
Управляющие конструкции
Условные операторы
if–then–elseif–else
if condition1 then
-- блок 1
elseif condition2 then
-- блок 2
else
-- блок по умолчанию
end
Условия должны быть выражениями. Только nil и false считаются ложными.
Циклы
while
while condition do
-- тело цикла
end
repeat–until
repeat
-- тело цикла
until condition
Тело цикла выполняется хотя бы один раз. Условие проверяется после каждой итерации.
for (числовой)
for i = start, limit, step do
-- тело
end
start,limit,step— числовые выражения.stepпо умолчанию равен1.- Переменная цикла
i— локальная и доступна только внутри цикла. - Изменение
iвнутри цикла не влияет на количество итераций.
for (обобщённый)
for key, value in iterator, state, control do
-- тело
end
Часто используется с pairs() и ipairs():
for k, v in pairs(table) do ... end
for i, v in ipairs(array) do ... end
Строки
Строки задаются в одинарных '...', двойных "..." или длинных скобках [[...]].
Длинные строки сохраняют пробелы и переносы. Можно использовать уровни вложенности: [=[ ... ]=], [==[ ... ]==] и так далее.
Экранирование:
\n— новая строка\t— табуляция\\— обратный слеш\",\'— кавычки\ddd— байт в десятичной системе (до трёх цифр)
Строки неизменяемы. Любая операция над строкой создаёт новую строку.
Комментарии
- Однострочные:
-- это комментарий - Многострочные:
--[[ это многострочный комментарий ]]
Многострочные комментарии могут быть отключены, если добавить символ - перед открывающей скобкой:
---[[ отключённый комментарий ]]
print("этот код выполняется")
Значения истинности
В Lua только два значения считаются ложными: nil и false.
Все остальные значения, включая 0, пустую строку "", пустую таблицу {}, считаются истинными.
Оператор # (длина)
Применяется к строкам и «массивоподобным» таблицам.
- Для строки — возвращает количество байтов.
- Для таблицы — возвращает длину последовательности, то есть наибольший целочисленный ключ
n, такой чтоt[1],t[2], ...,t[n]не равныnil, аt[n+1]равноnil.
Поведение неопределено, если таблица содержит «дыры» (пропущенные индексы).
Функции, замыкания, вызовы
Функции
Функция в Lua — это значение первого класса. Её можно присваивать переменным, передавать как аргумент, возвращать из других функций.
Объявление функции
Именованная функция (глобальная):
function greet(name)
return "Hello, " .. name
end
Это сокращение для:
greet = function(name)
return "Hello, " .. name
end
Локальная функция:
local function add(a, b)
return a + b
end
Это сокращение для:
local add
add = function(a, b)
return a + b
end
Такая форма позволяет рекурсивные вызовы внутри локальной функции.
Параметры и аргументы
Lua не проверяет количество переданных аргументов.
- Лишние аргументы игнорируются.
- Недостающим параметрам присваивается
nil.
Пример:
function f(a, b, c) print(a, b, c) end
f(1, 2) --> 1 2 nil
f(1, 2, 3, 4) --> 1 2 3
Вариадические функции (с переменным числом аргументов)
Обозначаются через ...:
function sum(...)
local args = {...}
local total = 0
for i = 1, #args do
total = total + args[i]
end
return total
end
Внутри функции ... представляет собой список значений. Его можно использовать напрямую:
function echo(...)
return ...
end
Оператор select помогает работать с ...:
select(n, ...)— возвращает все аргументы, начиная с n-го.select("#", ...)— возвращает количество аргументов (включаяnil).
Пример:
function first_arg(...)
return select(1, ...)
end
Возврат нескольких значений
Функция может возвращать несколько значений:
function coords()
return 10, 20
end
x, y = coords() -- x = 10, y = 20
Если вызов функции не находится в «расширяющем контексте» (например, не в списке присваивания или аргументов), возвращается только первое значение.
print(coords()) --> 10 20
local a = coords() --> a = 10
Расширяющий контекст включает:
- Множественное присваивание
- Список аргументов при вызове другой функции
- Конструктор таблицы
{ f() }— в этом случае все возвращённые значения вставляются
Исключение: последний элемент в списке инициализации таблицы не расширяется, если за ним следует запятая или закрывающая скобка без дополнительных элементов.
Хвостовая рекурсия (tail call)
Вызов функции в хвостовой позиции (последнее действие функции) оптимизируется: он не потребляет дополнительный стек.
function factorial(n, acc)
acc = acc or 1
if n <= 1 then
return acc
else
return factorial(n - 1, n * acc) -- хвостовой вызов
end
end
Такой вызов эквивалентен циклу и не вызывает переполнения стека даже при больших n.
Хвостовой вызов происходит только при точном синтаксисе:
return func(args)- без дополнительных операций после вызова
Недопустимо:
return func() + 1 -- не хвостовой
return g(func()) -- не хвостовой
Замыкания
Замыкание — это функция, захватывающая переменные из внешней области видимости.
function make_counter()
local count = 0
return function()
count = count + 1
return count
end
end
c1 = make_counter()
c2 = make_counter()
print(c1()) --> 1
print(c1()) --> 2
print(c2()) --> 1
Каждый вызов make_counter создаёт новую локальную переменную count, и каждая возвращённая функция сохраняет ссылку на свою копию.
Замыкания позволяют инкапсуляцию состояния без использования таблиц или объектов.
Анонимные функции
Функции без имени часто используются как аргументы:
table.sort(names, function(a, b) return a < b end)
Функции как методы
Lua не имеет встроенной поддержки объектов, но методы эмулируются через синтаксис :
obj = {
value = 42,
get = function(self)
return self.value
end
}
-- или
function obj:set(v)
self.value = v
end
Вызов:
obj:get() -- эквивалентно obj.get(obj)
obj:set(100) -- эквивалентно obj.set(obj, 100)
Синтаксис obj:method() автоматически передаёт obj как первый аргумент (self).
Стандартные функции высшего порядка
Lua не предоставляет встроенные функции map, filter, reduce, но их легко реализовать:
function map(t, fn)
local result = {}
for k, v in pairs(t) do
result[k] = fn(v)
end
return result
end
function filter(t, pred)
local result = {}
for k, v in pairs(t) do
if pred(v) then
table.insert(result, v)
end
end
return result
end
Обработка ошибок в функциях
Lua использует pcall и xpcall для безопасного вызова:
local success, result = pcall(some_function, arg1, arg2)
if success then
print("Успех:", result)
else
print("Ошибка:", result)
end
xpcall принимает обработчик ошибок:
xpcall(f, error_handler)
Функции и сборка мусора
Замыкания, анонимные функции и локальные переменные управляются сборщиком мусора. Циклические ссылки между таблицами и функциями разрешаются корректно благодаря инкрементальному сборщику мусора с поддержкой финализаторов (__gc).
Особенности производительности
- Вызов функции имеет небольшие накладные расходы.
- Локальные функции быстрее глобальных.
- Использование
localдля часто вызываемых функций (local assert = assert) ускоряет доступ. - Хвостовые вызовы не увеличивают глубину стека.
Таблицы, итераторы, метаметоды
Таблицы
Таблица — единственная встроенная структура данных в Lua. Она объединяет свойства массива, словаря, множества, объекта и модуля.
Создание таблиц
Пустая таблица:
t = {}
Таблица с начальными значениями:
point = { x = 10, y = 20 }
colors = { "red", "green", "blue" }
mixed = { name = "Alice", 42, true }
В конструкторе:
- Записи вида
key = valueсоздают хеш-часть. - Значения без ключа (
"red",42) добавляются в массивную часть с целочисленными ключами, начиная с 1.
Эквивалентные формы:
t = { x = 1 }
t = { ["x"] = 1 }
t = {}
t.x = 1
t["x"] = 1
Доступ к элементам
t.key -- синтаксический сахар для t["key"]
t[key] -- динамический доступ
t[100] -- числовой ключ
t[nil] -- допустимо, но не рекомендуется (ключ nil игнорируется)
Ключом может быть любое значение, кроме nil и NaN. Числа и строки наиболее распространены.
Изменение и удаление
t.new_field = "value"
t[10] = "ten"
t.existing = nil -- удаление поля
Присваивание nil удаляет пару «ключ–значение» из таблицы.
Длина таблицы
Оператор #t работает только с последовательностями — таблицами, где целочисленные ключи идут подряд от 1 до n без пропусков.
a = {10, 20, 30}
print(#a) --> 3
b = {10, nil, 30}
print(#b) --> 1 или 3 (неопределённое поведение)
Для произвольных таблиц используйте явный подсчёт:
function table_size(t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
end
Стандартные функции для работы с таблицами (table.*)
Модуль table предоставляет вспомогательные функции.
table.insert(list, [pos,] value)
Вставляет value в список list:
- Без
pos— в конец. - С
pos— на указанную позицию (остальные сдвигаются).
t = {1, 2}
table.insert(t, 3) --> {1, 2, 3}
table.insert(t, 2, 99) --> {1, 99, 2, 3}
table.remove(list, [pos])
Удаляет и возвращает элемент по индексу pos (по умолчанию — последний).
x = table.remove(t) --> удаляет последний
y = table.remove(t, 1) --> удаляет первый
table.concat(list, [sep], [i], [j])
Объединяет строковые представления элементов списка от i до j через разделитель sep.
t = {"a", "b", "c"}
s = table.concat(t, ", ") --> "a, b, c"
table.sort(list, [comp])
Сортирует список на месте. По умолчанию — по возрастанию.
table.sort(t, function(a, b) return a > b end) -- по убыванию
Функция сравнения должна быть транзитивной и не иметь побочных эффектов.
table.move(a1, f, e, t, a2)
Перемещает элементы из таблицы a1 с индексов [f, e] в таблицу a2, начиная с индекса t. Возвращает a2.
dest = {}
table.move(src, 1, 5, 1, dest)
table.unpack(list, [i], [j]) → ...
(В Lua 5.2–5.3 называлась unpack; в 5.4 перемещена в table.unpack.)
Возвращает элементы списка как отдельные значения.
x, y, z = table.unpack({10, 20, 30}) --> x=10, y=20, z=30
Итераторы
pairs(t)
Возвращает итератор по всем парам ключ–значение в таблице. Порядок обхода не определён.
for k, v in pairs(t) do
print(k, v)
end
ipairs(t)
Возвращает итератор по целочисленным ключам, начиная с 1, пока не встретится nil.
for i, v in ipairs(t) do
print(i, v)
end
Останавливается при первой «дыре».
Пользовательские итераторы
Итератор — это функция, возвращающая следующее значение при каждом вызове.
Пример: итератор по диапазону
function range(from, to)
local current = from - 1
return function()
current = current + 1
if current <= to then
return current
end
end
end
for i in range(1, 5) do print(i) end
Генератор итератора возвращает три значения: функцию, неизменяемое состояние и начальный контрольный элемент.
Метаметоды и метатаблицы
Метатаблица — таблица, определяющая поведение другой таблицы при определённых операциях.
Установка метатаблицы:
setmetatable(t, mt)
Получение метатаблицы:
mt = getmetatable(t)
Основные метаметоды
| Метаметод | Описание |
|---|---|
__index | Вызывается при чтении отсутствующего ключа. Может быть функцией или таблицей. |
__newindex | Вызывается при записи в отсутствующий или новый ключ. |
__add | Оператор + |
__sub | Оператор - |
__mul | Оператор * |
__div | Оператор / |
__mod | Оператор % |
__pow | Оператор ^ |
__unm | Унарный минус - |
__idiv | Целочисленное деление // |
__band | Побитовое И & |
__bor | Побитовое ИЛИ ` |
__bxor | Побитовое XOR ~ |
__bnot | Побитовое НЕ ~ (унарный) |
__shl | Сдвиг влево << |
__shr | Сдвиг вправо >> |
__concat | Конкатенация .. |
__len | Оператор # |
__eq | Оператор == |
__lt | Оператор < |
__le | Оператор <= |
__call | Вызов таблицы как функции t() |
__tostring | Преобразование в строку (например, при print) |
__gc | Финализатор (только для userdata в Lua 5.3+, для таблиц — начиная с Lua 5.4) |
__close | Используется в механизме toclose (Lua 5.4) |
Пример: вектор с перегруженными операторами
Vector = {}
Vector.__index = Vector
function Vector.new(x, y)
return setmetatable({x = x or 0, y = y or 0}, Vector)
end
function Vector.__add(a, b)
return Vector.new(a.x + b.x, a.y + b.y)
end
function Vector.__tostring(v)
return "(" .. v.x .. ", " .. v.y .. ")"
end
v1 = Vector.new(1, 2)
v2 = Vector.new(3, 4)
v3 = v1 + v2
print(v3) --> (4, 6)
__index как таблица
Часто используется для наследования:
parent = { value = 42 }
child = setmetatable({}, { __index = parent })
print(child.value) --> 42
__newindex для контроля записи
protected = {}
mt = {
__newindex = function(t, k, v)
error("Запись запрещена в " .. tostring(k))
end
}
setmetatable(protected, mt)
Реализация объектов и классов
Lua не имеет встроенных классов, но их легко эмулировать.
Прототипный стиль
Account = { balance = 0 }
Account.__index = Account
function Account:new(initial_balance)
local obj = { balance = initial_balance or 0 }
setmetatable(obj, self)
return obj
end
function Account:deposit(amount)
self.balance = self.balance + amount
end
a = Account:new(100)
a:deposit(50)
print(a.balance) --> 150
Классический стиль с закрытыми полями (через замыкания)
function new_account(initial)
local self = { balance = initial }
return {
deposit = function(amount) self.balance = self.balance + amount end,
get_balance = function() return self.balance end
}
end
Модули, загрузка кода, стандартная библиотека
Модули и система загрузки
Lua поддерживает модульную организацию кода через функцию require.
Создание модуля
Модуль — это таблица, возвращаемая из файла.
-- math_utils.lua
local M = {}
function M.add(a, b)
return a + b
end
function M.multiply(a, b)
return a * b
end
return M
Использование модуля
local utils = require("math_utils")
print(utils.add(2, 3)) --> 5
Функция require:
- Ищет модуль по имени (точки преобразуются в слеши:
mylib.utils→mylib/utils.lua). - Кэширует результат в таблице
package.loaded. - Не перезагружает модуль при повторном вызове.
Чтобы перезагрузить модуль, нужно очистить кэш:
package.loaded["math_utils"] = nil
Альтернативный стиль (устаревший, но встречающийся)
Ранее использовался глобальный подход:
-- старый стиль
local modname = ...
local M = {}
_G[modname] = M
Современный код использует return.
Пути поиска модулей
Пути задаются в:
package.path— для Lua-файлов (.lua)package.cpath— для C-библиотек (.so,.dll)
Формат пути:
?;?.lua;/usr/local/share/lua/5.4/?
Где ? заменяется на имя модуля.
Пример:
package.path = package.path .. ";./modules/?.lua"
Загрузка и выполнение кода
Lua позволяет динамически загружать и выполнять строки кода.
load(string, [chunkname], [mode], [env])
Возвращает функцию или ошибку.
string— код как строка.chunkname— имя чанка (для отладки).mode—"t"(текст),"b"(бинарный),"bt"(оба).env— окружение (таблица, используемая как_ENV).
Пример:
local f, err = load("return x + 1", "adder", "t", { x = 10 })
if f then
print(f()) --> 11
end
loadfile(filename, [mode], [env])
Аналог load, но читает код из файла.
dofile(filename)
Выполняет файл немедленно:
dofile("config.lua") -- эквивалентно loadfile + вызов
Не кэшируется и не использует require.
Окружение (_ENV)
Начиная с Lua 5.2, глобальные переменные разрешаются через неявную локальную переменную _ENV.
По умолчанию:
_ENV = _G
Можно переопределить окружение для блока:
local custom_env = { print = print, x = 42 }
local f = load("print(x)", "test", "t", custom_env)
f() --> 42
Или внутри файла:
-- restricted.lua
local _ENV = { print = print }
x = 10 -- запись в локальное окружение
print(x) -- работает
print(y) -- ошибка, если y не в _ENV
Это позволяет создавать песочницы.
Стандартная библиотека
Lua поставляется с набором встроенных модулей.
string — работа со строками
Основные функции:
string.len(s)— длина строки (эквивалент#s)string.sub(s, i, j)— подстрока сiпоj(отрицательные индексы — с конца)string.char(...)— создаёт строку из кодов символовstring.byte(s, [i], [j])— возвращает коды символовstring.rep(s, n)— повторяет строкуnразstring.reverse(s)— переворачивает строкуstring.upper(s),string.lower(s)— регистрstring.format(fmt, ...)— форматирование (аналогprintf)
Шаблонные функции (pattern matching):
string.find(s, pattern, [init], [plain])— поиск позицииstring.match(s, pattern, [init])— извлечение по шаблонуstring.gsub(s, pattern, repl, [n])— заменаstring.gmatch(s, pattern)— итератор по совпадениям
Шаблоны:
.— любой символ%a— буква%d— цифра%w— буква или цифра%s— пробельный символ%p— знак препинания%u— заглавная буква%l— строчная буква%x— шестнадцатеричная цифра%z— нулевой байт
Квантификаторы:
+— один или более*— ноль или более-— ноль или более (нежадный)?— ноль или один
Группировка: ( ) — захватывающие скобки.
Пример:
date = "2026-01-17"
year, month, day = string.match(date, "(%d+)-(%d+)-(%d+)")
math — математические функции
math.pi,math.hugemath.abs,math.sqrt,math.sin,math.cos,math.tan,math.atan,math.atan2math.exp,math.log,math.log10math.floor,math.ceil,math.modfmath.min,math.maxmath.random([m], [n])— генератор случайных чиселmath.randomseed(x)— установка зерна
В Lua 5.3+ добавлены целочисленные функции:
math.type(x)—"integer"или"float"math.tointeger(x)— преобразование в целоеmath.ult(m, n)— беззнаковое сравнение
io — ввод-вывод
io.input([file])— установка входного потокаio.output([file])— установка выходного потокаio.read(...)— чтение из текущего входного потокаio.write(...)— запись в текущий выходной потокio.open(filename, mode)— открытие файлаio.close(file)— закрытиеio.lines([filename])— итератор по строкам файла
Режимы открытия:
"r"— чтение"w"— запись (очистка)"a"— добавление"r+"— чтение и запись"w+"— чтение и запись (очистка)
os — системные операции
os.date([format], [time])— форматирование датыos.time([table])— временная меткаos.difftime(t2, t1)— разница во времениos.execute(command)— выполнение команды ОСos.exit([code])— завершение программыos.getenv(varname)— получение переменной окруженияos.remove(filename)— удаление файлаos.rename(old, new)— переименование
coroutine — корутины (сопрограммы)
coroutine.create(f)— создаёт корутинуcoroutine.resume(co, ...)— запускает или возобновляетcoroutine.yield(...)— приостанавливает и возвращает значенияcoroutine.status(co)—"running","suspended","dead","normal"coroutine.isyieldable()— можно ли вызватьyieldв текущем контексте
Пример:
co = coroutine.create(function()
for i = 1, 3 do
coroutine.yield(i)
end
end)
print(coroutine.resume(co)) --> true, 1
print(coroutine.resume(co)) --> true, 2
print(coroutine.resume(co)) --> true, 3
print(coroutine.resume(co)) --> true
debug — отладка и интроспекция
Используется редко в production, но мощен для инструментов.
debug.getinfo(func, [what])— информация о функцииdebug.getlocal(thread, level, local)— имя и значение локальной переменнойdebug.setlocal(...)debug.getupvalue(func, up)debug.setupvalue(func, up, value)debug.traceback([thread], [message], [level])— стек вызовов
utf8 (Lua 5.3+)
Работа с UTF-8 строками:
utf8.len(s)— количество Unicode-символовutf8.char(...)— строка из кодовых точекutf8.codepoint(s, [i], [j])— итератор по кодовым точкамutf8.offset(s, n, [i])— позиция n-го символа
Особенности встраивания Lua
Lua часто используется как встраиваемый язык. В этом случае:
- Хост-приложение управляет жизненным циклом
lua_State. - C-функции регистрируются в Lua через
lua_pushcfunction. - Таблицы и userdata передают данные между C и Lua.
- Метаметоды
__gcпозволяют освобождать ресурсы. - Функция
lua_pcallобеспечивает безопасный вызов с обработкой ошибок.
Пример регистрации:
static int l_add(lua_State *L) {
double a = lua_tonumber(L, 1);
double b = lua_tonumber(L, 2);
lua_pushnumber(L, a + b);
return 1;
}
// Регистрация
lua_register(L, "add", l_add);
Особенности Lua 5.4, управление памятью, стиль и производительность
Ключевые нововведения в Lua 5.4
Lua 5.4 (выпущена в 2020 году) внесла несколько важных улучшений по сравнению с 5.3 и 5.2.
1. Константные локальные переменные (<const>)
Можно объявить локальную переменную как константу:
local <const> MAX_SIZE = 1000
Попытка присвоить новое значение вызывает ошибку на этапе компиляции.
Такие переменные могут содержать только примитивные значения: nil, boolean, number, string.
2. Механизм toclose и метаметод __close
Позволяет автоматически освобождать ресурсы при выходе из блока.
local f <const> = io.open("data.txt", "r")
local data <toclose> = f
-- файл будет автоматически закрыт при выходе из блока
Работает для любого значения, чья метатаблица содержит __close:
local mt = {
__close = function(obj)
print("Очистка:", obj.name)
end
}
local resource <toclose> = setmetatable({ name = "test" }, mt)
Это аналог defer в Go или деструкторов в RAII.
3. Улучшенный сборщик мусора
- Новый режим инкрементальной сборки с адаптивной скоростью.
- Поддержка поколений (generational mode) — экспериментальный режим, оптимизированный для короткоживущих объектов.
- Возможность явного управления через
collectgarbage():"stop"— остановить"restart"— возобновить"collect"— полная сборка"count"— размер памяти в КБ"step"— один шаг инкрементальной сборки"setpause","setstepmul"— настройка поведения
4. Целочисленный тип как отдельная категория
Хотя внешне integer и float остаются подтипами number, внутри они различаются. Это улучшает производительность и точность.
Функция math.type(x) возвращает "integer" или "float".
5. Безопасное сравнение беззнаковых целых
Функция math.ult(m, n) сравнивает два целых числа как беззнаковые.
6. Улучшенная обработка ошибок
- Больше контекста в трассировке стека.
- Поддержка пользовательских обработчиков ошибок в
xpcall.
Управление памятью
Lua использует автоматическое управление памятью на основе инкрементального сборщика мусора с возможностью поколений.
Жизненный цикл объекта
- Объект создаётся (таблица, строка, функция и т.д.).
- Пока на него есть ссылки — он жив.
- Когда все ссылки исчезают — он становится недостижимым.
- Сборщик мусора освобождает память.
Финализаторы (__gc)
Для userdata (и таблиц, начиная с Lua 5.4) можно задать метаметод __gc:
local mt = {
__gc = function(obj)
print("Освобождение ресурса:", obj.id)
end
}
local obj = setmetatable({ id = 123 }, mt)
obj = nil
collectgarbage() -- вызовет __gc
Важно: финализаторы не гарантируют немедленного вызова. Они вызываются во время сборки мусора.
Советы по снижению давления на GC
- Избегать создания временных таблиц и строк в горячих циклах.
- Переиспользовать объекты, где возможно.
- Использовать
table.clear(t)(Lua 5.4) вместоt = {}, если таблица большая. - Минимизировать замыкания в циклах.
Стиль кода и лучшие практики
Именование
- Переменные и функции:
snake_case - Константы:
UPPER_SNAKE_CASE - Локальные переменные: всегда объявлять с
local - Избегать глобальных переменных
Структура файла
- Один модуль — один файл.
- Возвращать таблицу в конце.
- Не смешивать исполняемый код и определения функций.
Обработка ошибок
- Использовать
assertдля внутренних проверок. - Использовать
pcall/xpcallдля внешних вызовов. - Возвращать
nil, error_messageв случае ошибки (идиома Lua).
Пример:
function divide(a, b)
if b == 0 then
return nil, "division by zero"
end
return a / b
end
Производительность
- Доступ к локальным переменным быстрее, чем к глобальным.
- Кэшировать часто используемые функции:
local table_insert = table.insert - Избегать
..в циклах — использоватьtable.concat - Использовать
ipairsдля массивов,pairs— для хешей - Не вызывать
#tв цикле — сохранить в переменную
Совместимость между версиями
| Фича | Lua 5.1 | Lua 5.2 | Lua 5.3 | Lua 5.4 |
|---|---|---|---|---|
_ENV | нет | да | да | да |
goto | нет | да | да | да |
| Целые числа | нет | нет | да | да |
bit32 | да | да | удалён | — |
utf8 | нет | нет | да | да |
<const> | нет | нет | нет | да |
<toclose> | нет | нет | нет | да |
__gc для таблиц | нет | нет | нет | да |
table.move | нет | нет | да | да |
table.unpack | unpack | unpack → table.unpack | table.unpack | table.unpack |
Для максимальной переносимости:
- Избегать версионно-специфичных фич.
- Проверять
LUA_VERSION_NUMпри необходимости. - Использовать
loadвместоloadstring(устарело в 5.2).
Распространённые ловушки
-
Глобальные переменные по ошибке
local t = { x = 1 }
t.x = 2 -- правильно
t.x = 2 -- опечатка: t.x = 2 vs t.x = 2 — но если написать t.x = 2 как t.x = 2, всё ок
-- опасность: mistyped = 10 -- глобальная!Решение: использовать строгий режим через метатаблицу
_G. -
Изменение таблицы во время итерации
pairs
Добавление/удаление ключей во времяpairsдопустимо, но может привести к пропуску или повторному обходу элементов. -
Неправильное использование
#для таблиц с дырами
Всегда проверяйте, что таблица — сплошной массив. -
Замыкания в циклах
funcs = {}
for i = 1, 3 do
funcs[i] = function() print(i) end
end
-- все функции выведут 4 (значение i после цикла)Решение: захватывать значение через параметр:
funcs[i] = function() return function() print(i) end end ()
-- или
for i = 1, 3 do
local i = i
funcs[i] = function() print(i) end
end